﻿using System.Collections.ObjectModel;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace HMEngine.HMInputs {
    public enum HMGamePadButton {
        A = 0,
        B = 1,
        Back = 2,
        LeftShoulder = 3,
        LeftStick = 4,
        RightShoulder = 5,
        RightStick = 6,
        Start = 7,
        X = 8,
        Y = 9
    }

    public enum HMGamePadDPadDirection {
        Down = 0,
        Left = 1,
        Right = 2,
        Up = 3
    }

    public delegate void HMGamePadPressedHandler(Collection<HMGamePadButton> buttons);
    public delegate void HMGamePadReleasedHandler(Collection<HMGamePadButton> buttons);
    public delegate void HMGamePadHeldHandler(Collection<HMGamePadButton> buttons);
    public delegate void HMGamePadDPadPressedHandler(Collection<HMGamePadDPadDirection> directions);
    public delegate void HMGamePadDPadReleasedHandler(Collection<HMGamePadDPadDirection> directions);
    public delegate void HMGamePadDPadHeldHandler(Collection<HMGamePadDPadDirection> directions);
    public delegate void HMGamePadJoystickHandler(Vector2 leftStick, Vector2 rightStick);

    public class HMGamePadDevice : HMInputDevice {
        private readonly PlayerIndex playerIndex;
        private GamePadState gamePadState;
        
        private readonly Collection<HMPressedState> buttonStates;
        private readonly Collection<HMPressedState> dButtonStates;
        private Collection<ButtonState> pressedStates;
        private Collection<ButtonState> dPressedStates;

        private Collection<HMGamePadButton> pressed;
        private Collection<HMGamePadButton> held;
        private Collection<HMGamePadButton> released;
        private Collection<HMGamePadDPadDirection> dpadPressed;
        private Collection<HMGamePadDPadDirection> dpadHeld;
        private Collection<HMGamePadDPadDirection> dpadReleased;

        private Vector2 ltStickMove;
        private Vector2 rtStickMove;

        public event HMGamePadPressedHandler OnButtonPressed;
        public event HMGamePadReleasedHandler OnButtonReleased;
        public event HMGamePadHeldHandler OnButtonHeld;
        public event HMGamePadDPadPressedHandler OnDPadPressed;
        public event HMGamePadDPadReleasedHandler OnDPadReleased;
        public event HMGamePadDPadHeldHandler OnDPadHeld;
        public event HMGamePadJoystickHandler OnJoystickMoved;

        public HMGamePadDevice(PlayerIndex player) {
            playerIndex = player;

            gamePadState = GamePad.GetState(playerIndex);
            buttonStates = new Collection<HMPressedState>();
            dButtonStates = new Collection<HMPressedState>();
            for (int i = 0; i < 10; i++) {
                buttonStates.Add(HMPressedState.Idle);
            }

            for (int i = 0; i < 4; i++) {
                dButtonStates.Add(HMPressedState.Idle);
            }
        }

        internal override void Update() {
            // Button checks including the DPad
            gamePadState = GamePad.GetState(playerIndex);
            pressed = new Collection<HMGamePadButton>();
            released = new Collection<HMGamePadButton>();
            held = new Collection<HMGamePadButton>();
            dpadPressed = new Collection<HMGamePadDPadDirection>();
            dpadReleased = new Collection<HMGamePadDPadDirection>();
            dpadHeld = new Collection<HMGamePadDPadDirection>();
            pressedStates = GamePadPressedStateArray(gamePadState);
            for (int i = 0; i < 10; i++) {
                buttonStates[i] = CheckPressedState(pressedStates[i], buttonStates[i]);
            }
            dPressedStates = DPadPressedStateArray(gamePadState.DPad);
            for (int i = 0; i < 4; i++) {
                dButtonStates[i] = CheckPressedState(dPressedStates[i], dButtonStates[i]);
            }

            // Checking the Joysticks for movement
            ltStickMove = new Vector2(gamePadState.ThumbSticks.Left.X, gamePadState.ThumbSticks.Left.Y);
            rtStickMove = new Vector2(gamePadState.ThumbSticks.Right.X, gamePadState.ThumbSticks.Right.Y);

            // Loading Event Lists
            for (int i = 0; i < 10; i++) {
                if (buttonStates[i] == HMPressedState.Pressed) pressed.Add((HMGamePadButton)i);
                else if (buttonStates[i] == HMPressedState.Held) held.Add((HMGamePadButton)i);
                else if (buttonStates[i] == HMPressedState.Released) released.Add((HMGamePadButton)i);
            }
            for (int i = 0; i < 4; i++) {
                if (dButtonStates[i] == HMPressedState.Pressed) dpadPressed.Add((HMGamePadDPadDirection)i);
                else if (dButtonStates[i] == HMPressedState.Held) dpadHeld.Add((HMGamePadDPadDirection)i);
                else if (dButtonStates[i] == HMPressedState.Released)
                    dpadReleased.Add((HMGamePadDPadDirection)i);
            }

            // Event Calls
            if (pressed.Count > 0 && null != OnButtonPressed) OnButtonPressed(pressed);
            if (released.Count > 0 && null != OnButtonReleased) OnButtonReleased(released);
            if (held.Count > 0 && null != OnButtonHeld) OnButtonHeld(held);
            if (dpadPressed.Count > 0 && null != OnDPadPressed) OnDPadPressed(dpadPressed);
            if (dpadReleased.Count > 0 && null != OnDPadReleased) OnDPadReleased(dpadReleased);
            if (dpadHeld.Count > 0 && null != OnDPadHeld) OnDPadHeld(dpadHeld);
            if (ltStickMove.Length() + rtStickMove.Length() > 0 && null != OnJoystickMoved)
                OnJoystickMoved(ltStickMove, rtStickMove);
        }

        internal static Collection<ButtonState> GamePadPressedStateArray(GamePadState gamePad) {
            Collection<ButtonState> states = new Collection<ButtonState> {
                gamePad.Buttons.A,
                gamePad.Buttons.B,
                gamePad.Buttons.Back,
                gamePad.Buttons.LeftShoulder,
                gamePad.Buttons.LeftStick,
                gamePad.Buttons.RightShoulder,
                gamePad.Buttons.RightStick,
                gamePad.Buttons.Start,
                gamePad.Buttons.X,
                gamePad.Buttons.Y
            };

            return states;
        }

        internal static Collection<ButtonState> DPadPressedStateArray(GamePadDPad dpad) {
            Collection<ButtonState> states = new Collection<ButtonState> {
                dpad.Down,
                dpad.Left,
                dpad.Right,
                dpad.Up
            };

            return states;
        }
    }
}